using SharpDepend.Datatypes;
using System;

interface BatchItem
{
    int TextureID { get; set; }
    Texture Texture { get; set; }
    float Depth { get; set; }
    OpenTK.Vector4 Color { get; set; }
    int Indices { get; }

    float[] GetVertices(ref int length);
    float[] GetColors(ref int length);
    ushort[] AddIndices(ushort offset, ref int length);
    float[] GetTextureCoords(ref int length);
}

class RectangleSpriteBatchItem : BatchItem
{
    private float[] mVertices;
    private float[] mTextCoords;
    private float[] mColors;
    private ushort[] mIndices;

    public int Indices { get { return mIndices.Length; } }

    public RectangleSpriteBatchItem()
    {
        mVertices = new float[8];

        int i = -1;
        mTextCoords = new float[8];
        mTextCoords[++i] = 0; mTextCoords[++i] = 0;
        mTextCoords[++i] = 1; mTextCoords[++i] = 0;
        mTextCoords[++i] = 1; mTextCoords[++i] = 1;
        mTextCoords[++i] = 0; mTextCoords[++i] = 1;

        mColors = new float[16];

        i = -1;
        mIndices = new ushort[6];
        mIndices[++i] = 0;
        mIndices[++i] = 1;
        mIndices[++i] = 2;

        mIndices[++i] = 2;
        mIndices[++i] = 3;
        mIndices[++i] = 0;
    }

    public int TextureID { get; set; }

    public Texture Texture { get; set; }

    public float Depth { get; set; }

    public OpenTK.Vector4 Color
    {
        get { return new OpenTK.Vector4(mColors[0], mColors[1], mColors[2], mColors[3]); }
        set
        {
            mColors[0] = mColors[4] = mColors[8] = mColors[12] = value.X;
            mColors[1] = mColors[5] = mColors[9] = mColors[13] = value.Y;
            mColors[2] = mColors[6] = mColors[10] = mColors[14] = value.Z;
            mColors[3] = mColors[7] = mColors[11] = mColors[15] = value.W;
        }
    }

    public OpenTK.Vector2 vertexTL
    {
        get { return new OpenTK.Vector2(mVertices[0], mVertices[1]); }
        set
        {
            mVertices[0] = value.X;
            mVertices[1] = value.Y;
        }
    }
    public OpenTK.Vector2 vertexTR
    {
        get { return new OpenTK.Vector2(mVertices[2], mVertices[3]); }
        set
        {
            mVertices[2] = value.X;
            mVertices[3] = value.Y;
        }
    }
    public OpenTK.Vector2 vertexBR
    {
        get { return new OpenTK.Vector2(mVertices[4], mVertices[5]); }
        set
        {
            mVertices[4] = value.X;
            mVertices[5] = value.Y;
        }
    }
    public OpenTK.Vector2 vertexBL
    {
        get { return new OpenTK.Vector2(mVertices[6], mVertices[7]); }
        set
        {
            mVertices[6] = value.X;
            mVertices[7] = value.Y;
        }
    }

    public OpenTK.Vector2 TexCoordTL
    {
        get { return new OpenTK.Vector2(mTextCoords[0], mTextCoords[1]); }
        set
        {
            mTextCoords[0] = value.X;
            mTextCoords[1] = value.Y;

            mTextCoords[3] = value.Y;
            mTextCoords[6] = value.X;
        }
    }
    public OpenTK.Vector2 TexCoordBR
    {
        get { return new OpenTK.Vector2(mTextCoords[2], mTextCoords[3]); }
        set
        {
            mTextCoords[4] = value.X;
            mTextCoords[5] = value.Y;

            mTextCoords[2] = value.X;
            mTextCoords[7] = value.Y;
        }
    }

    public float[] GetVertices(ref int length)
    {
        length = mVertices.Length;
        return mVertices;
    }

    public float[] GetColors(ref int length)
    {
        length = mColors.Length;
        return mColors;
    }

    public ushort[] AddIndices(ushort offset, ref int length)
    {
        mIndices[0] = (ushort)(0 + offset);
        mIndices[1] = (ushort)(1 + offset);
        mIndices[2] = (ushort)(2 + offset);
        mIndices[3] = (ushort)(2 + offset);
        mIndices[4] = (ushort)(3 + offset);
        mIndices[5] = (ushort)(0 + offset);

        length = mIndices.Length;
        return mIndices;
    }

    public float[] GetTextureCoords(ref int length)
    {
        length = mTextCoords.Length;
        return mTextCoords;
    }
}

class LineSpriteBatchItem : BatchItem
{
    private float[] mVertices;
    private float[] mTextCoords;
    private float[] mColors;
    private ushort[] mIndices;

    public int Indices { get { return mIndices.Length; } }

    public LineSpriteBatchItem()
    {
        mVertices = new float[8];

        int i = -1;
        mTextCoords = new float[8];
        mTextCoords[++i] = 0; mTextCoords[++i] = 0;
        mTextCoords[++i] = 1; mTextCoords[++i] = 0;
        mTextCoords[++i] = 1; mTextCoords[++i] = 1;
        mTextCoords[++i] = 0; mTextCoords[++i] = 1;

        mColors = new float[16];

        i = -1;
        mIndices = new ushort[6];
        mIndices[++i] = 0;
        mIndices[++i] = 1;
        mIndices[++i] = 2;

        mIndices[++i] = 2;
        mIndices[++i] = 3;
        mIndices[++i] = 0;
    }

    private OpenTK.Vector2 mStart;
    private OpenTK.Vector2 mStop;

    public OpenTK.Vector2 Start
    {
        get
        {
            return mStart;
        }
        set
        {
            mStart = value;

            OpenTK.Vector2 tl = value;
            OpenTK.Vector2 bl = value;

            mVertices[0] = tl.X;
            mVertices[1] = tl.Y;
            mVertices[3] = bl.X;
            mVertices[4] = bl.Y;
        }
    }

    public OpenTK.Vector2 Stop
    {
        get
        {
            return mStop;
        }
        set
        {
            mStop = value;

            OpenTK.Vector2 tr = value;
            OpenTK.Vector2 br = value;
            OpenTK.Vector2 tl = new OpenTK.Vector2(mVertices[0], mVertices[1]);
            OpenTK.Vector2 bl = new OpenTK.Vector2(mVertices[3], mVertices[4]);

            CalculateLine(ref tl, ref bl, ref tr, ref br);

            mVertices[2] = tr.X;
            mVertices[3] = tr.Y;
            mVertices[4] = br.X;
            mVertices[5] = br.Y;

            mVertices[0] = tl.X;
            mVertices[1] = tl.Y;
            mVertices[6] = bl.X;
            mVertices[7] = bl.Y;
        }
    }

    private static void CalculateLine(ref OpenTK.Vector2 tl, ref OpenTK.Vector2 bl,
        ref OpenTK.Vector2 tr, ref OpenTK.Vector2 br)
    {
        double xDiff = tr.X - tl.X;
        double yDiff = tr.Y - tl.Y;
        double value = -Math.Atan2(yDiff, xDiff);

        tl.X += (float)(Math.Sin(value) * 1.0);
        tl.Y += (float)(Math.Cos(value) * 1.0);
        bl.X -= (float)(Math.Sin(value) * 1.0);
        bl.Y -= (float)(Math.Cos(value) * 1.0);

        tr.X += (float)(Math.Sin(value) * 1.0);
        tr.Y += (float)(Math.Cos(value) * 1.0);
        br.X -= (float)(Math.Sin(value) * 1.0);
        br.Y -= (float)(Math.Cos(value) * 1.0);
    }

    public int TextureID { get; set; }

    public Texture Texture { get; set; }

    public float Depth { get; set; }

    public OpenTK.Vector4 Color
    {
        get { return new OpenTK.Vector4(mColors[0], mColors[1], mColors[2], mColors[3]); }
        set
        {
            mColors[0] = mColors[4] = mColors[8] = mColors[12] = value.X;
            mColors[1] = mColors[5] = mColors[9] = mColors[13] = value.Y;
            mColors[2] = mColors[6] = mColors[10] = mColors[14] = value.Z;
            mColors[3] = mColors[7] = mColors[11] = mColors[15] = value.W;
        }
    }

    public float[] GetVertices(ref int length)
    {
        length = mVertices.Length;
        return mVertices;
    }

    public float[] GetColors(ref int length)
    {
        length = mColors.Length;
        return mColors;
    }

    public ushort[] AddIndices(ushort offset, ref int length)
    {
        mIndices[0] = (ushort)(0 + offset);
        mIndices[1] = (ushort)(1 + offset);
        mIndices[2] = (ushort)(2 + offset);
        mIndices[3] = (ushort)(2 + offset);
        mIndices[4] = (ushort)(3 + offset);
        mIndices[5] = (ushort)(0 + offset);

        length = mIndices.Length;
        return mIndices;
    }

    public float[] GetTextureCoords(ref int length)
    {
        length = mTextCoords.Length;
        return mTextCoords;
    }
}

class RectangleTrianglesBatchItem : BatchItem
{
    private FastStructList<Vector2> mVerticesIn = new FastStructList<Vector2>(4);
    private FastStructList<Color> mColorsIn = new FastStructList<Color>(4);
    private FastStructList<ushort> mIndicesIn = new FastStructList<ushort>(6);

    private FastStructList<float> mVerticesOut = new FastStructList<float>(4);
    private FastStructList<float> mColorsOut = new FastStructList<float>(4);
    private FastStructList<ushort> mIndicesOut = new FastStructList<ushort>(6);

    private int mVerticesIn_length;
    private int mColorsIn_length;
    private int mIndicesIn_length;

    public int Indices { get { return mIndicesIn_length; } }

    /// <summary>
    /// Not used. Use SetColors() instead.
    /// </summary>
    public OpenTK.Vector4 Color { get; set; }

    public float Depth { get; set; }

    public void SetIndices(FastStructList<ushort> indices)
    {
        mIndicesIn_length = indices.Count;
        //mIndicesIn = indices;
        //
        //if (mIndicesOut == null || indices.Count > mIndicesOut.Length)
        //{
        //    mIndicesOut = new ushort[indices.Count];
        //}

        mIndicesIn.Clear();
        mIndicesIn.AddRange(indices);
    }

    public void SetVertices(FastStructList<Vector2> vertices)
    {
        mVerticesIn_length = vertices.Count * 2;
        //mVerticesIn = vertices;
        //
        //if (mVerticesOut == null || mVerticesIn_length > mVerticesOut.Length)
        //{
        //    mVerticesOut = new float[mVerticesIn_length];
        //}

        mVerticesIn.Clear();
        mVerticesIn.AddRange(vertices);
    }

    public void SetColors(FastStructList<Color> colors)
    {
        mColorsIn_length = colors.Count * 4;
        //mColorsIn = colors;
        //
        //if (mColorsOut == null || mColorsIn_length > mColorsOut.Length)
        //{
        //    mColorsOut = new float[mColorsIn_length];
        //}

        mColorsIn.Clear();
        mColorsIn.AddRange(colors);
    }

    public int TextureID { get; set; }

    public Texture Texture { get; set; }

    public ushort[] AddIndices(ushort offset, ref int length)
    {
        mIndicesOut.SetLength(mIndicesIn.Count);
        for (int i = mIndicesIn.Count - 1; i >= 0; i--)
        {
            mIndicesOut[i] = (ushort)(mIndicesIn[i] + offset);
        }

        length = mIndicesIn_length;
        return mIndicesOut.ToArray();
    }

    public float[] GetColors(ref int length)
    {
        mColorsOut.SetLength(mColorsIn_length);
        for (int i = mColorsIn_length - 1; i >= 0; i -= 4)
        {
            mColorsOut[i-3] = mColorsIn[i / 4].R * 0.0039215686274509803921568627451f;
            mColorsOut[i-2] = mColorsIn[i / 4].G * 0.0039215686274509803921568627451f;
            mColorsOut[i-1] = mColorsIn[i / 4].B * 0.0039215686274509803921568627451f;
            mColorsOut[i-0] = mColorsIn[i / 4].A * 0.0039215686274509803921568627451f;
        }


        length = mColorsIn_length;
        return mColorsOut.ToArray();
    }

    public float[] GetVertices(ref int length)
    {
        mVerticesOut.SetLength(mVerticesIn_length);
        for (int i = mVerticesIn_length - 1; i >= 0; i -= 2)
        {
            mVerticesOut[i - 0] = (float)mVerticesIn[i/2].Y;
            mVerticesOut[i - 1] = (float)mVerticesIn[i/2].X;
        }

        length = mVerticesIn_length;
        return mVerticesOut.ToArray();
    }

    public float[] GetTextureCoords(ref int length)
    {
        length = mVerticesIn_length;
        return mVerticesOut.ToArray();
    }
}